library(tidyverse) # for data wrangling
library(car) # for regression diagnostics
library(broom) # for tidy output
library(ggfortify) # for model diagnostics
library(knitr) # for kable
library(emmeans) # for estimating marginal means
library(MASS) # for glm.nb
library(brms)
library(broom.mixed)
library(tidybayes)
library(bayesplot)
library(standist) # for visualizing distributions
library(rstanarm)
library(cmdstanr) # for cmdstan
library(ggeffects)
library(rstan)
library(DHARMa)
library(ggridges)
library(easystats) # framework for stats, modelling and visualisation
library(patchwork)
library(modelsummary)
source("helperFunctions.R")Bayesian GLMM Part4
1 Preparations
Load the necessary libraries
2 Scenario
To investigate synergistic coral defence by mutualist crustaceans, Mckeon et al. (2012) conducted an aquaria experiment in which colonies of a coral species were placed in a tank along with a predatory sea star and one of four symbiont combinations:
- no symbiont,
- a crab symbiont
- a shrimp symbiont
- both a crab and shrimp symbiont.
The experiments were conducted in a large octagonal flow-through seawater tank that was partitioned into eight sections, which thereby permitted two of each of the four symbiont combinations to be observed concurrently. The tank was left overnight and in the morning, the presence of feeding scars on each coral colony was scored as evidence of predation. The experiments were repeated ten times, each time with fresh coral colonies, sea stars and symbiont.
The ten experimental times represent blocks (random effects) within which the symbiont type (fixed effect) are nested.
3 Read in the data
Rows: 80 Columns: 3
── Column specification ────────────────────────────────────────────────────────
Delimiter: ","
chr (1): SYMBIONT
dbl (2): BLOCK, PREDATION
ℹ Use `spec()` to retrieve the full column specification for this data.
ℹ Specify the column types or set `show_col_types = FALSE` to quiet this message.
spc_tbl_ [80 × 3] (S3: spec_tbl_df/tbl_df/tbl/data.frame)
$ BLOCK : num [1:80] 1 1 2 2 3 3 4 4 5 5 ...
$ PREDATION: num [1:80] 0 1 1 1 1 1 1 1 1 1 ...
$ SYMBIONT : chr [1:80] "none" "none" "none" "none" ...
- attr(*, "spec")=
.. cols(
.. BLOCK = col_double(),
.. PREDATION = col_double(),
.. SYMBIONT = col_character()
.. )
- attr(*, "problems")=<externalptr>
mckeon (80 rows and 3 variables, 3 shown)
ID | Name | Type | Missings | Values | N
---+-----------+-----------+----------+---------+-----------
1 | BLOCK | numeric | 0 (0.0%) | [1, 10] | 80
---+-----------+-----------+----------+---------+-----------
2 | PREDATION | numeric | 0 (0.0%) | 0 | 30 (37.5%)
| | | | 1 | 50 (62.5%)
---+-----------+-----------+----------+---------+-----------
3 | SYMBIONT | character | 0 (0.0%) | both | 20 (25.0%)
| | | | crabs | 20 (25.0%)
| | | | none | 20 (25.0%)
| | | | shrimp | 20 (25.0%)
------------------------------------------------------------
Since the response here is the presence or absence of predation (feeding scars), a binomial distribution is appropriate.
We need to make sure that the categorical variable and the random effect are defined as factors. When doing so, it might be valuable to rearrange the order of the fixed effect (SYMBIONT) such that the none group is considered the first group. This way, the other levels will all naturally be compared to this level (hence it will be treated as a reference of control group).
4 Exploratory data analysis
Model formula: \[ y_i \sim{} \mathcal{N}(n, p_i)\\ ln\left(\frac{p_i}{1-p_1}\right) =\boldsymbol{\beta} \bf{X_i} + \boldsymbol{\gamma} \bf{Z_i} \]
where \(\boldsymbol{\beta}\) and \(\boldsymbol{\gamma}\) are vectors of the fixed and random effects parameters respectively and \(\bf{X}\) is the model matrix representing the overall intercept and effects of symbionts on the probability of the colony experiencing predation. \(\bf{Z}\) represents a cell means model matrix for the random intercepts associated with individual coral colonies.
5 Fit the model
In rstanarm, the default priors are designed to be weakly informative. They are chosen to provide moderate regularisation (to help prevent over-fitting) and help stabilise the computations.
Priors for model '.'
------
Intercept (after predictors centered)
~ normal(location = 0, scale = 2.5)
Coefficients
Specified prior:
~ normal(location = [0,0,0], scale = [2.5,2.5,2.5])
Adjusted prior:
~ normal(location = [0,0,0], scale = [5.74,5.74,5.74])
Covariance
~ decov(reg. = 1, conc. = 1, shape = 1, scale = 1)
------
See help('prior_summary.stanreg') for more details
This tells us:
for the intercept (when the family is binomial), a normal prior with a mean of 0 and a standard deviation of 2.5 is used. These are the default priors for bernoulli models. A mean of 0 on a logit scale corresponds to a probability of 0.5. Hence the prior expected value is 0.5.
for the coefficients (in this case, just the slope), the default prior is a normal prior centred around 0 with a standard deviation of 2.5. For binomial models, this is then adjusted for the scale of the data by dividing the 2.5 by the standard deviation of the predictor (then rounded).
Lets now run with priors only so that we can explore the range of values they allow in the posteriors.
You are calculating adjusted predictions on the population-level (i.e.
`type = "fixed"`) for a *generalized* linear mixed model.
This may produce biased estimates due to Jensen's inequality. Consider
setting `bias_correction = TRUE` to correct for this bias.
See also the documentation of the `bias_correction` argument.
Note: uncertainty of error terms are not taken into account. Consider
setting `interval` to "prediction". This will call `posterior_predict()`
instead of `posterior_epred()`.
Conclusions:
- it is very difficult to assess the priors from the predicted posteriors when the posteriors are on the response scale as they will always approach values of 0 and 1 (no matter how wide they are on the link scale).
Interestingly, if we instead use ggemmeans, it will (perhaps erroneously) generate the partial effects on the link scale (yet with an incorrectly labelled y-axis). Probability scale values of 0.99 and 0.01 (very large and small respectively) correspond to value of approximately 4.6 and -4.6 respectively on the logit scale.
In the following partial plot, the routine attempts to convert the y-axes tick marks into percentages. Hence a value of 0.1 is converted to 10%. Consequently, values of -5 and 5 are labelled as -500% and 500% respectively. We know that on the probability scale, values must asymptote towards 0 and 1. Posterior intervals beyond -5 and 5 might be considered wide.
Alternatively, we could create the plot ourselves…
mckeon.rstanarm1 %>%
emmeans(~SYMBIONT, type = "link") %>%
as.data.frame() %>%
ggplot(aes(y = emmean, x = SYMBIONT)) +
geom_hline(yintercept = c(5, -5), linetype = "dashed") +
geom_pointrange(aes(ymin = lower.HPD, ymax = upper.HPD)) +
geom_point(
data = mckeon, aes(y = PREDATION),
position = position_jitter(width = 0.2, height = 0),
alpha = 0.4, color = "red"
)The following link provides some guidance about defining priors. [https://github.com/stan-dev/stan/wiki/Prior-Choice-Recommendations]
When defining our own priors, we typically do not want them to be scaled.
If we wanted to define our own priors that were less vague, yet still not likely to bias the outcomes, we could try the following priors (mainly plucked out of thin air):
- \(\beta_0\): normal centred at 0 (since this is 0.5 on the logit scale) with a standard deviation of 2
- mean of 0: since
logit(0.5) - alternatively, an argument could be made for a mean of 0.51: since
logit(mean(mckeon$PREDATION)) - sd of 2: since this not too large on logit scale
- mean of 0: since
- \(\beta_1\): normal centred at 0 with a standard deviation of 0.5
- sd of 0.5: since
model.matrix(~SYMBIONT, data=mckeon)[,-1] %>% apply(2,sd)
- sd of 0.5: since
- \(\Sigma\): decov with:
- regularisation: the exponent for a LKJ prior on the correlation matrix. A value of 1 (default) implies a joint uniform prior
- concentration: the concentration parameter for a symmetric Dirichlet distribution. A value of 1 (default) implies a joint uniform distribution
- shape and scale: the shape and scale parameters for a gamma prior on the scale and scale parameters of the decov prior. A value of 1 for both (default) simplifies the gamma prior to a unit-exponential distribution.
I will also overlay the raw data for comparison.
mckeon.rstanarm2 <- stan_glmer(PREDATION ~ SYMBIONT + (1 | BLOCK),
data = mckeon,
family = binomial(link = "logit"),
prior_intercept = normal(0, 2.5, autoscale = FALSE),
prior = normal(0, 6, autoscale = FALSE),
prior_covariance = decov(1, 1, 1, 1),
prior_PD = TRUE,
iter = 5000,
warmup = 1000,
chains = 3,
thin = 5,
refresh = 0
)Note: uncertainty of error terms are not taken into account. Consider
setting `interval` to "prediction". This will call `posterior_predict()`
instead of `posterior_epred()`.
Now lets refit, conditioning on the data.
posterior_vs_prior(mckeon.rstanarm3,
color_by = "vs", group_by = TRUE,
facet_args = list(scales = "free_y")
)
Drawing from prior...
Conclusions:
- in each case, the prior is substantially wider than the posterior, suggesting that the posterior is not biased towards the prior.
Data points may overlap. Use the `jitter` argument to add some amount of
random variation to the location of data points and avoid overplotting.
Note: uncertainty of error terms are not taken into account. Consider
setting `interval` to "prediction". This will call `posterior_predict()`
instead of `posterior_epred()`.
Data points may overlap. Use the `jitter` argument to add some amount of
random variation to the location of data points and avoid overplotting.
In brms, the default priors are designed to be weakly informative. They are chosen to provide moderate regularisation (to help prevent over fitting) and help stabilise the computations.
Unlike rstanarm, brms models must be compiled before they start sampling. For most models, the compilation of the stan code takes around 45 seconds.
mckeon.form <- bf(PREDATION | trials(1) ~ SYMBIONT + (1|BLOCK),
family=binomial(link='logit'))
#OR
mckeon.form <- bf(PREDATION ~ SYMBIONT + (1|BLOCK),
family=bernoulli(link='logit'))
options(width=150)
mckeon.form |> get_prior(data = mckeon) prior class coef group resp dpar nlpar lb ub source
(flat) b default
(flat) b SYMBIONTboth (vectorized)
(flat) b SYMBIONTcrabs (vectorized)
(flat) b SYMBIONTshrimp (vectorized)
student_t(3, 0, 2.5) Intercept default
student_t(3, 0, 2.5) sd 0 default
student_t(3, 0, 2.5) sd BLOCK 0 (vectorized)
student_t(3, 0, 2.5) sd Intercept BLOCK 0 (vectorized)
The following link provides some guidance about defining priors. [https://github.com/stan-dev/stan/wiki/Prior-Choice-Recommendations]
When defining our own priors, we typically do not want them to be scaled.
If we wanted to define our own priors that were less vague, yet still not likely to bias the outcomes, we could try the following priors (mainly plucked out of thin air):
- \(\beta_0\): normal centred at 0 (since this is 0.5 on the logit scale) with a standard deviation of 2.5
- mean of 0: since
logit(0.5) - sd of 2: since this not too large on logit scale
- alternatively, we could potentially use
logistic(0,1)
- mean of 0: since
- \(\beta_1\): normal centred at 0 with a standard deviation of 6
- sd of 6: since
2.5/model.matrix(~SYMBIONT, data=mckeon) %>% apply(2,sd)
- sd of 6: since
- \(\sigma_j\): half-cauchy with parameters 0 and 2.
- \(\Sigma\): decov with:
- regularisation: the exponent for a LKJ prior on the correlation matrix. A value of 1 (default) implies a joint uniform prior
- concentration: the concentration parameter for a symmetric Dirichlet distribution. A value of 1 (default) implies a joint uniform distribution
- shape and scale: the shape and scale parameters for a gamma prior on the scale and scale parameters of the decov prior. A value of 1 for both (default) simplifies the gamma prior to a unit-exponential distribution.
Note, for hierarchical models, the model will tend to want to have a large \(sigma\) in order to fit the data better. It is a good idea to regularise this tendency by applying a prior that has most mass around zero. Suitable candidates include:
- half-t: as the degrees of freedom approach infinity, this will approach a half-normal
- half-cauchy: this is essentially a half-t with 1 degree of freedom
- exponential
I will also overlay the raw data for comparison.
mckeon |>
group_by(SYMBIONT) |>
summarise(
Mean = logit(mean(PREDATION)),
Median = logit(median(PREDATION)),
sd = logit(abs(sd(PREDATION)))
)# A tibble: 4 × 4
SYMBIONT Mean Median sd
<fct> <dbl> <dbl> <dbl>
1 none 2.20 Inf -0.810
2 crabs 0.405 Inf 0.0105
3 shrimp 0.201 Inf 0.0417
4 both -0.201 -Inf 0.0417
(Intercept) SYMBIONTcrabs SYMBIONTshrimp SYMBIONTboth
Inf 5.737305 5.737305 5.737305
mckeon |>
group_by(SYMBIONT) |>
summarise(
mean_response = mean(PREDATION),
mad_response = mad(PREDATION),
sd_response = sd(PREDATION),
) |>
mutate(
mean_logit = logit(mean_response),
# Delta method approximation
sd_logit = sd_response / (mean_response * (1 - mean_response))
)# A tibble: 4 × 6
SYMBIONT mean_response mad_response sd_response mean_logit sd_logit
<fct> <dbl> <dbl> <dbl> <dbl> <dbl>
1 none 0.9 0 0.308 2.20 3.42
2 crabs 0.6 0 0.503 0.405 2.09
3 shrimp 0.55 0 0.510 0.201 2.06
4 both 0.45 0 0.510 -0.201 2.06
mckeon.form <- bf(PREDATION | trials(1) ~ SYMBIONT + (1 | BLOCK),
family = binomial(link = "logit")
)
# OR
mckeon.form <- bf(PREDATION ~ SYMBIONT + (1 | BLOCK),
family = bernoulli(link = "logit")
)
get_prior(mckeon.form, data = mckeon) prior class coef group resp dpar nlpar lb ub
(flat) b
(flat) b SYMBIONTboth
(flat) b SYMBIONTcrabs
(flat) b SYMBIONTshrimp
student_t(3, 0, 2.5) Intercept
student_t(3, 0, 2.5) sd 0
student_t(3, 0, 2.5) sd BLOCK 0
student_t(3, 0, 2.5) sd Intercept BLOCK 0
source
default
(vectorized)
(vectorized)
(vectorized)
default
default
(vectorized)
(vectorized)
priors <-
prior(normal(0, 2.5), class = "Intercept") +
prior(normal(0, 3), class = "b") +
prior(student_t(3, 0, 1.5), class = "sd")
mckeon.brm2 <- brm(mckeon.form,
data = mckeon,
prior = priors,
sample_prior = "only",
iter = 5000,
warmup = 1000,
chains = 3, cores = 3,
thin = 5,
control = list(adapt_delta = 0.99, max_treedepth = 20),
refresh = 0,
backend = "rstan"
)Compiling Stan program...
Start sampling
Note: uncertainty of error terms are not taken into account. Consider
setting `interval` to "prediction". This will call `posterior_predict()`
instead of `posterior_epred()`.
Warning in glmmTMB::glmmTMB(formula = cond_formula, family = family, data =
data): some components missing from 'family': downstream methods may fail
Warning in glmmTMB::glmmTMB(formula = cond_formula, family = family, data =
data): some components missing from 'family': downstream methods may fail
Data points may overlap. Use the `jitter` argument to add some amount of
random variation to the location of data points and avoid overplotting.
Warning in glmmTMB::glmmTMB(formula = cond_formula, family = family, data =
data): some components missing from 'family': downstream methods may fail
Warning in glmmTMB::glmmTMB(formula = cond_formula, family = family, data =
data): some components missing from 'family': downstream methods may fail
Warning in glmmTMB::glmmTMB(formula = cond_formula, family = family, data =
data): some components missing from 'family': downstream methods may fail
Warning in glmmTMB::glmmTMB(formula = cond_formula, family = family, data =
data): some components missing from 'family': downstream methods may fail
Data points may overlap. Use the `jitter` argument to add some amount of
random variation to the location of data points and avoid overplotting.
mckeon.brm2 %>%
emmeans(~SYMBIONT, type = "link") %>%
as.data.frame() %>%
ggplot(aes(y = emmean, x = SYMBIONT)) +
geom_hline(yintercept = c(5, -5), linetype = "dashed") +
geom_pointrange(aes(ymin = lower.HPD, ymax = upper.HPD)) +
geom_point(
data = mckeon, aes(y = PREDATION),
position = position_jitter(width = 0.2, height = 0),
alpha = 0.4, color = "red"
)Warning: Method 'parse_bf' is deprecated. Please use 'brmsterms' instead.
Now lets refit, conditioning on the data.
The desired updates require recompiling the model
Compiling Stan program...
Start sampling
Note: uncertainty of error terms are not taken into account. Consider
setting `interval` to "prediction". This will call `posterior_predict()`
instead of `posterior_epred()`.
Warning in glmmTMB::glmmTMB(formula = cond_formula, family = family, data =
data): some components missing from 'family': downstream methods may fail
Warning in glmmTMB::glmmTMB(formula = cond_formula, family = family, data =
data): some components missing from 'family': downstream methods may fail
Data points may overlap. Use the `jitter` argument to add some amount of
random variation to the location of data points and avoid overplotting.
Warning in glmmTMB::glmmTMB(formula = cond_formula, family = family, data =
data): some components missing from 'family': downstream methods may fail
Warning in glmmTMB::glmmTMB(formula = cond_formula, family = family, data =
data): some components missing from 'family': downstream methods may fail
Warning in glmmTMB::glmmTMB(formula = cond_formula, family = family, data =
data): some components missing from 'family': downstream methods may fail
Warning in glmmTMB::glmmTMB(formula = cond_formula, family = family, data =
data): some components missing from 'family': downstream methods may fail
Data points may overlap. Use the `jitter` argument to add some amount of
random variation to the location of data points and avoid overplotting.
mckeon.brm3 %>%
emmeans(~SYMBIONT, type = "link") %>%
as.data.frame() %>%
ggplot(aes(y = emmean, x = SYMBIONT)) +
geom_hline(yintercept = c(5, -5), linetype = "dashed") +
geom_pointrange(aes(ymin = lower.HPD, ymax = upper.HPD)) +
geom_point(
data = mckeon, aes(y = PREDATION),
position = position_jitter(width = 0.2, height = 0),
alpha = 0.4, color = "red"
)Warning: Method 'parse_bf' is deprecated. Please use 'brmsterms' instead.
[1] "b_Intercept" "b_SYMBIONTcrabs" "b_SYMBIONTshrimp"
[4] "b_SYMBIONTboth" "sd_BLOCK__Intercept" "Intercept"
[7] "r_BLOCK[1,Intercept]" "r_BLOCK[2,Intercept]" "r_BLOCK[3,Intercept]"
[10] "r_BLOCK[4,Intercept]" "r_BLOCK[5,Intercept]" "r_BLOCK[6,Intercept]"
[13] "r_BLOCK[7,Intercept]" "r_BLOCK[8,Intercept]" "r_BLOCK[9,Intercept]"
[16] "r_BLOCK[10,Intercept]" "prior_Intercept" "prior_b"
[19] "prior_sd_BLOCK" "lprior" "lp__"
[22] "accept_stat__" "stepsize__" "treedepth__"
[25] "n_leapfrog__" "divergent__" "energy__"
While we are here, we might like to explore a random intercept/slope model
mckeon.form <- bf(PREDATION | trials(1) ~ SYMBIONT + (SYMBIONT | BLOCK),
family = binomial(link = "logit")
)
# OR
mckeon.form <- bf(PREDATION ~ SYMBIONT + (SYMBIONT | BLOCK),
family = bernoulli(link = "logit")
)
get_prior(mckeon.form, mckeon) prior class coef group resp dpar nlpar lb ub
(flat) b
(flat) b SYMBIONTboth
(flat) b SYMBIONTcrabs
(flat) b SYMBIONTshrimp
lkj(1) cor
lkj(1) cor BLOCK
student_t(3, 0, 2.5) Intercept
student_t(3, 0, 2.5) sd 0
student_t(3, 0, 2.5) sd BLOCK 0
student_t(3, 0, 2.5) sd Intercept BLOCK 0
student_t(3, 0, 2.5) sd SYMBIONTboth BLOCK 0
student_t(3, 0, 2.5) sd SYMBIONTcrabs BLOCK 0
student_t(3, 0, 2.5) sd SYMBIONTshrimp BLOCK 0
source
default
(vectorized)
(vectorized)
(vectorized)
default
(vectorized)
default
default
(vectorized)
(vectorized)
(vectorized)
(vectorized)
(vectorized)
## As there are not many observations, the following might be too ambitious without
## stronger priors
priors <-
prior(normal(0, 2.5), class = "Intercept") +
prior(normal(0, 3), class = "b") +
## prior(student_t(3,0,1.5), class = 'sd', coef = "Intercept", group = "BLOCK") +
prior(student_t(3, 0, 1.5), class = "sd") +
prior(lkj_corr_cholesky(1), class = "cor")
mckeon.brm4 <- brm(mckeon.form,
data = mckeon,
prior = priors,
sample_prior = "yes",
iter = 5000,
warmup = 1000,
chains = 3, cores = 3,
thin = 5,
refresh = 0,
control = list(adapt_delta = 0.99, max_treedepth = 20),
backend = "rstan"
)Compiling Stan program...
Start sampling
[1] "b_Intercept"
[2] "b_SYMBIONTcrabs"
[3] "b_SYMBIONTshrimp"
[4] "b_SYMBIONTboth"
[5] "sd_BLOCK__Intercept"
[6] "sd_BLOCK__SYMBIONTcrabs"
[7] "sd_BLOCK__SYMBIONTshrimp"
[8] "sd_BLOCK__SYMBIONTboth"
[9] "cor_BLOCK__Intercept__SYMBIONTcrabs"
[10] "cor_BLOCK__Intercept__SYMBIONTshrimp"
[11] "cor_BLOCK__SYMBIONTcrabs__SYMBIONTshrimp"
[12] "cor_BLOCK__Intercept__SYMBIONTboth"
[13] "cor_BLOCK__SYMBIONTcrabs__SYMBIONTboth"
[14] "cor_BLOCK__SYMBIONTshrimp__SYMBIONTboth"
[15] "Intercept"
[16] "r_BLOCK[1,Intercept]"
[17] "r_BLOCK[2,Intercept]"
[18] "r_BLOCK[3,Intercept]"
[19] "r_BLOCK[4,Intercept]"
[20] "r_BLOCK[5,Intercept]"
[21] "r_BLOCK[6,Intercept]"
[22] "r_BLOCK[7,Intercept]"
[23] "r_BLOCK[8,Intercept]"
[24] "r_BLOCK[9,Intercept]"
[25] "r_BLOCK[10,Intercept]"
[26] "r_BLOCK[1,SYMBIONTcrabs]"
[27] "r_BLOCK[2,SYMBIONTcrabs]"
[28] "r_BLOCK[3,SYMBIONTcrabs]"
[29] "r_BLOCK[4,SYMBIONTcrabs]"
[30] "r_BLOCK[5,SYMBIONTcrabs]"
[31] "r_BLOCK[6,SYMBIONTcrabs]"
[32] "r_BLOCK[7,SYMBIONTcrabs]"
[33] "r_BLOCK[8,SYMBIONTcrabs]"
[34] "r_BLOCK[9,SYMBIONTcrabs]"
[35] "r_BLOCK[10,SYMBIONTcrabs]"
[36] "r_BLOCK[1,SYMBIONTshrimp]"
[37] "r_BLOCK[2,SYMBIONTshrimp]"
[38] "r_BLOCK[3,SYMBIONTshrimp]"
[39] "r_BLOCK[4,SYMBIONTshrimp]"
[40] "r_BLOCK[5,SYMBIONTshrimp]"
[41] "r_BLOCK[6,SYMBIONTshrimp]"
[42] "r_BLOCK[7,SYMBIONTshrimp]"
[43] "r_BLOCK[8,SYMBIONTshrimp]"
[44] "r_BLOCK[9,SYMBIONTshrimp]"
[45] "r_BLOCK[10,SYMBIONTshrimp]"
[46] "r_BLOCK[1,SYMBIONTboth]"
[47] "r_BLOCK[2,SYMBIONTboth]"
[48] "r_BLOCK[3,SYMBIONTboth]"
[49] "r_BLOCK[4,SYMBIONTboth]"
[50] "r_BLOCK[5,SYMBIONTboth]"
[51] "r_BLOCK[6,SYMBIONTboth]"
[52] "r_BLOCK[7,SYMBIONTboth]"
[53] "r_BLOCK[8,SYMBIONTboth]"
[54] "r_BLOCK[9,SYMBIONTboth]"
[55] "r_BLOCK[10,SYMBIONTboth]"
[56] "prior_Intercept"
[57] "prior_b"
[58] "prior_sd_BLOCK"
[59] "prior_cor_BLOCK"
[60] "lprior"
[61] "lp__"
[62] "accept_stat__"
[63] "stepsize__"
[64] "treedepth__"
[65] "n_leapfrog__"
[66] "divergent__"
[67] "energy__"
Family: bernoulli
Links: mu = logit
Formula: PREDATION ~ SYMBIONT + (SYMBIONT | BLOCK)
Data: mckeon (Number of observations: 80)
Draws: 3 chains, each with iter = 5000; warmup = 1000; thin = 5;
total post-warmup draws = 2400
Multilevel Hyperparameters:
~BLOCK (Number of levels: 10)
Estimate Est.Error l-95% CI u-95% CI Rhat
sd(Intercept) 2.17 1.23 0.22 5.00 1.00
sd(SYMBIONTcrabs) 3.26 3.22 0.13 11.05 1.00
sd(SYMBIONTshrimp) 2.36 2.11 0.10 7.67 1.00
sd(SYMBIONTboth) 2.32 2.28 0.08 8.10 1.00
cor(Intercept,SYMBIONTcrabs) 0.30 0.39 -0.55 0.89 1.00
cor(Intercept,SYMBIONTshrimp) 0.26 0.41 -0.65 0.90 1.00
cor(SYMBIONTcrabs,SYMBIONTshrimp) 0.37 0.42 -0.62 0.92 1.01
cor(Intercept,SYMBIONTboth) 0.20 0.42 -0.68 0.88 1.00
cor(SYMBIONTcrabs,SYMBIONTboth) 0.28 0.42 -0.64 0.90 1.00
cor(SYMBIONTshrimp,SYMBIONTboth) 0.32 0.43 -0.68 0.92 1.00
Bulk_ESS Tail_ESS
sd(Intercept) 1694 1594
sd(SYMBIONTcrabs) 1494 2196
sd(SYMBIONTshrimp) 1988 2271
sd(SYMBIONTboth) 2014 2200
cor(Intercept,SYMBIONTcrabs) 2103 2266
cor(Intercept,SYMBIONTshrimp) 2298 2181
cor(SYMBIONTcrabs,SYMBIONTshrimp) 1944 2144
cor(Intercept,SYMBIONTboth) 2345 2364
cor(SYMBIONTcrabs,SYMBIONTboth) 2091 2204
cor(SYMBIONTshrimp,SYMBIONTboth) 2152 2370
Regression Coefficients:
Estimate Est.Error l-95% CI u-95% CI Rhat Bulk_ESS Tail_ESS
Intercept 3.07 1.16 1.15 5.65 1.00 2055 2410
SYMBIONTcrabs -1.43 1.68 -4.36 2.26 1.00 2110 2145
SYMBIONTshrimp -2.19 1.46 -4.89 0.92 1.00 1775 1925
SYMBIONTboth -3.32 1.45 -6.31 -0.38 1.00 2093 2178
Draws were sampled using sampling(NUTS). For each parameter, Bulk_ESS
and Tail_ESS are effective sample size measures, and Rhat is the potential
scale reduction factor on split chains (at convergence, Rhat = 1).
mckeon.brm4 %>%
posterior_samples() %>%
dplyr::select(-`lp__`) %>%
pivot_longer(everything(), names_to = "key") %>%
filter(!str_detect(key, "^r")) %>%
mutate(
Type = ifelse(str_detect(key, "prior"), "Prior", "Posterior"),
## Class = ifelse(str_detect(key, 'Intercept'), 'Intercept',
## ifelse(str_detect(key, 'b'), 'b', 'sigma')),
Class = case_when(
str_detect(key, "(^b|^prior).*Intercept$") ~ "Intercept",
str_detect(key, "b_SYMBIONT.*|prior_b_SYMBIONT.*") &
!str_detect(key, ".*:.*") ~ "SYMBIONT",
str_detect(key, "sd") ~ "sd",
str_detect(key, "^cor|prior_cor") ~ "cor",
str_detect(key, "sigma") ~ "sigma"
),
Par = str_replace(key, "b_", "")
) %>%
ggplot(aes(x = Type, y = value, color = Par)) +
stat_pointinterval(position = position_dodge()) +
facet_wrap(~Class, scales = "free")Warning: Method 'posterior_samples' is deprecated. Please see ?as_draws for
recommended alternatives.
We can compare the two models using LOO
Warning: Found 1 observations with a pareto_k > 0.7 in model 'mckeon.brm3'. We
recommend to set 'moment_match = TRUE' in order to perform moment matching for
problematic observations.
Computed from 2400 by 80 log-likelihood matrix.
Estimate SE
elpd_loo -27.8 6.9
p_loo 8.2 2.5
looic 55.5 13.9
------
MCSE of elpd_loo is NA.
MCSE and ESS estimates assume MCMC draws (r_eff in [0.8, 1.1]).
Pareto k diagnostic values:
Count Pct. Min. ESS
(-Inf, 0.7] (good) 79 98.8% 297
(0.7, 1] (bad) 1 1.2% <NA>
(1, Inf) (very bad) 0 0.0% <NA>
See help('pareto-k-diagnostic') for details.
Warning: Found 4 observations with a pareto_k > 0.7 in model 'mckeon.brm4'. We
recommend to set 'moment_match = TRUE' in order to perform moment matching for
problematic observations.
Computed from 2400 by 80 log-likelihood matrix.
Estimate SE
elpd_loo -26.4 6.5
p_loo 10.7 3.4
looic 52.7 13.0
------
MCSE of elpd_loo is NA.
MCSE and ESS estimates assume MCMC draws (r_eff in [0.7, 1.1]).
Pareto k diagnostic values:
Count Pct. Min. ESS
(-Inf, 0.7] (good) 76 95.0% 385
(0.7, 1] (bad) 3 3.8% <NA>
(1, Inf) (very bad) 1 1.2% <NA>
See help('pareto-k-diagnostic') for details.
elpd_diff se_diff
mckeon.brm4 0.0 0.0
mckeon.brm3 -1.4 1.2
It appears that the random intercept/slope model is marginally better than the simpler random intercept model.
mckeon.brm4 %>%
posterior_samples() %>%
dplyr::select(-`lp__`) %>%
pivot_longer(everything(), names_to = "key") %>%
filter(!str_detect(key, "^r")) %>%
mutate(
Type = ifelse(str_detect(key, "prior"), "Prior", "Posterior"),
Class = case_when(
str_detect(key, "(^b|^prior).*Intercept$") ~ "Intercept",
str_detect(key, "b_SYMBIONT.*|prior_b") ~ "TREATMENT",
str_detect(key, "sd") ~ "sd",
str_detect(key, "^cor|prior_cor") ~ "cor",
str_detect(key, "sigma") ~ "sigma"
),
Par = str_replace(key, "b_", "")
) %>%
ggplot(aes(x = Type, y = value, color = Par)) +
stat_pointinterval(position = position_dodge(), show.legend = FALSE) +
facet_wrap(~Class, scales = "free")Warning: Method 'posterior_samples' is deprecated. Please see ?as_draws for
recommended alternatives.
6 MCMC sampling diagnostics
The bayesplot package offers a range of MCMC diagnostics as well as Posterior Probability Checks (PPC), all of which have a convenient plot() interface. Lets start with the MCMC diagnostics.
bayesplot MCMC module:
mcmc_acf
mcmc_acf_bar
mcmc_areas
mcmc_areas_ridges
mcmc_combo
mcmc_dens
mcmc_dens_chains
mcmc_dens_overlay
mcmc_hex
mcmc_hist
mcmc_hist_by_chain
mcmc_intervals
mcmc_neff
mcmc_neff_hist
mcmc_nuts_acceptance
mcmc_nuts_divergence
mcmc_nuts_energy
mcmc_nuts_stepsize
mcmc_nuts_treedepth
mcmc_pairs
mcmc_parcoord
mcmc_rank_ecdf
mcmc_rank_hist
mcmc_rank_overlay
mcmc_recover_hist
mcmc_recover_intervals
mcmc_recover_scatter
mcmc_rhat
mcmc_rhat_hist
mcmc_scatter
mcmc_trace
mcmc_trace_highlight
mcmc_violin
Of these, we will focus on:
- trace: this plots the estimates of each parameter over the post-warmup length of each MCMC chain. Each chain is plotted in a different shade of blue, with each parameter in its own facet. Ideally, each trace should just look like noise without any discernible drift and each of the traces for a specific parameter should look the same (i.e, should not be displaced above or below any other trace for that parameter).
pars <- mckeon.brm3 |> get_variables()
pars <- pars %>%
str_extract("^b.Intercept|^b_SYMBIONT.*|[sS]igma|^sd.*") %>%
na.omit()
pars[1] "b_Intercept" "b_SYMBIONTcrabs" "b_SYMBIONTshrimp"
[4] "b_SYMBIONTboth" "sd_BLOCK__Intercept"
attr(,"na.action")
[1] 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
attr(,"class")
[1] "omit"
Warning: The following arguments were unrecognized and ignored: variables
No divergences to plot.
# OR
mckeon.brm3 %>% mcmc_plot(
type = "trace",
variable = "^b.Intercept|^b_SYMBIONT.*|[sS]igma|^sd.*",
regex = TRUE
)No divergences to plot.
The chains appear well mixed and very similar
- acf_bar (auto-correlation function): plots the auto-correlation between successive MCMC sample lags for each parameter and each chain
## OR
mckeon.brm3 %>% mcmc_plot(
type = "acf_bar",
variable = "^b.Intercept|^b_SYMBIONT.*|[sS]igma|^sd.*",
regex = TRUE
)There is no evidence of auto-correlation in the MCMC samples
- rhat_hist: Rhat is a scale reduction factor measure of convergence between the chains. The closer the values are to 1, the more the chains have converged. Values greater than 1.05 indicate a lack of convergence. There will be an Rhat value for each parameter estimated.
`stat_bin()` using `bins = 30`. Pick better value with `binwidth`.
All Rhat values are below 1.05, suggesting the chains have converged.
neff_hist (number of effective samples): the ratio of the number of effective samples (those not rejected by the sampler) to the number of samples provides an indication of the effectiveness (and efficiency) of the MCMC sampler. Ratios that are less than 0.5 for a parameter suggest that the sampler spent considerable time in difficult areas of the sampling domain and rejected more than half of the samples (replacing them with the previous effective sample).
If the ratios are low, tightening the priors may help.
`stat_bin()` using `bins = 30`. Pick better value with `binwidth`.
Ratios all very high.
The rstan package offers a range of MCMC diagnostics. Lets start with the MCMC diagnostics.
Of these, we will focus on:
- stan_trace: this plots the estimates of each parameter over the post-warmup length of each MCMC chain. Each chain is plotted in a different colour, with each parameter in its own facet. Ideally, each trace should just look like noise without any discernible drift and each of the traces for a specific parameter should look the same (i.e, should not be displaced above or below any other trace for that parameter).
[1] "b_Intercept"
[2] "b_SYMBIONTcrabs"
[3] "b_SYMBIONTshrimp"
[4] "b_SYMBIONTboth"
[5] "sd_BLOCK__Intercept"
[6] "sd_BLOCK__SYMBIONTcrabs"
[7] "sd_BLOCK__SYMBIONTshrimp"
[8] "sd_BLOCK__SYMBIONTboth"
[9] "cor_BLOCK__Intercept__SYMBIONTcrabs"
[10] "cor_BLOCK__Intercept__SYMBIONTshrimp"
[11] "cor_BLOCK__SYMBIONTcrabs__SYMBIONTshrimp"
[12] "cor_BLOCK__Intercept__SYMBIONTboth"
[13] "cor_BLOCK__SYMBIONTcrabs__SYMBIONTboth"
[14] "cor_BLOCK__SYMBIONTshrimp__SYMBIONTboth"
[15] "Intercept"
[16] "r_BLOCK[1,Intercept]"
[17] "r_BLOCK[2,Intercept]"
[18] "r_BLOCK[3,Intercept]"
[19] "r_BLOCK[4,Intercept]"
[20] "r_BLOCK[5,Intercept]"
[21] "r_BLOCK[6,Intercept]"
[22] "r_BLOCK[7,Intercept]"
[23] "r_BLOCK[8,Intercept]"
[24] "r_BLOCK[9,Intercept]"
[25] "r_BLOCK[10,Intercept]"
[26] "r_BLOCK[1,SYMBIONTcrabs]"
[27] "r_BLOCK[2,SYMBIONTcrabs]"
[28] "r_BLOCK[3,SYMBIONTcrabs]"
[29] "r_BLOCK[4,SYMBIONTcrabs]"
[30] "r_BLOCK[5,SYMBIONTcrabs]"
[31] "r_BLOCK[6,SYMBIONTcrabs]"
[32] "r_BLOCK[7,SYMBIONTcrabs]"
[33] "r_BLOCK[8,SYMBIONTcrabs]"
[34] "r_BLOCK[9,SYMBIONTcrabs]"
[35] "r_BLOCK[10,SYMBIONTcrabs]"
[36] "r_BLOCK[1,SYMBIONTshrimp]"
[37] "r_BLOCK[2,SYMBIONTshrimp]"
[38] "r_BLOCK[3,SYMBIONTshrimp]"
[39] "r_BLOCK[4,SYMBIONTshrimp]"
[40] "r_BLOCK[5,SYMBIONTshrimp]"
[41] "r_BLOCK[6,SYMBIONTshrimp]"
[42] "r_BLOCK[7,SYMBIONTshrimp]"
[43] "r_BLOCK[8,SYMBIONTshrimp]"
[44] "r_BLOCK[9,SYMBIONTshrimp]"
[45] "r_BLOCK[10,SYMBIONTshrimp]"
[46] "r_BLOCK[1,SYMBIONTboth]"
[47] "r_BLOCK[2,SYMBIONTboth]"
[48] "r_BLOCK[3,SYMBIONTboth]"
[49] "r_BLOCK[4,SYMBIONTboth]"
[50] "r_BLOCK[5,SYMBIONTboth]"
[51] "r_BLOCK[6,SYMBIONTboth]"
[52] "r_BLOCK[7,SYMBIONTboth]"
[53] "r_BLOCK[8,SYMBIONTboth]"
[54] "r_BLOCK[9,SYMBIONTboth]"
[55] "r_BLOCK[10,SYMBIONTboth]"
[56] "prior_Intercept"
[57] "prior_b"
[58] "prior_sd_BLOCK"
[59] "prior_cor_BLOCK"
[60] "lprior"
[61] "lp__"
[62] "accept_stat__"
[63] "stepsize__"
[64] "treedepth__"
[65] "n_leapfrog__"
[66] "divergent__"
[67] "energy__"
pars <- mckeon.brm4 |> get_variables()
pars <- str_extract(pars, "^b_.*|^sigma$|^sd.*") |> na.omit()
mckeon.brm4$fit |>
stan_trace(pars = pars)The chains appear well mixed and very similar
- stan_acf (auto-correlation function): plots the auto-correlation between successive MCMC sample lags for each parameter and each chain
There is no evidence of auto-correlation in the MCMC samples
- stan_rhat: Rhat is a scale reduction factor measure of convergence between the chains. The closer the values are to 1, the more the chains have converged. Values greater than 1.05 indicate a lack of convergence. There will be an Rhat value for each parameter estimated.
All Rhat values are below 1.05, suggesting the chains have converged.
stan_ess (number of effective samples): the ratio of the number of effective samples (those not rejected by the sampler) to the number of samples provides an indication of the effectiveness (and efficiency) of the MCMC sampler. Ratios that are less than 0.5 for a parameter suggest that the sampler spent considerable time in difficult areas of the sampling domain and rejected more than half of the samples (replacing them with the previous effective sample).
If the ratios are low, tightening the priors may help.
Ratios all very high.
The ggmean package also as a set of MCMC diagnostic functions. Lets start with the MCMC diagnostics.
Of these, we will focus on:
- ggs_traceplot: this plots the estimates of each parameter over the post-warmup length of each MCMC chain. Each chain is plotted in a different colour, with each parameter in its own facet. Ideally, each trace should just look like noise without any discernible drift and each of the traces for a specific parameter should look the same (i.e, should not be displaced above or below any other trace for that parameter).
The chains appear well mixed and very similar
- gss_autocorrelation (autocorrelation function): plots the autocorrelation between successive MCMC sample lags for each parameter and each chain
There is no evidence of auto-correlation in the MCMC samples
- stan_rhat: Rhat is a scale reduction factor measure of convergence between the chains. The closer the values are to 1, the more the chains have converged. Values greater than 1.05 indicate a lack of convergence. There will be an Rhat value for each parameter estimated.
All Rhat values are below 1.05, suggesting the chains have converged.
stan_ess (number of effective samples): the ratio of the number of effective samples (those not rejected by the sampler) to the number of samples provides an indication of the effectiveness (and efficiency) of the MCMC sampler. Ratios that are less than 0.5 for a parameter suggest that the sampler spent considerable time in difficult areas of the sampling domain and rejected more than half of the samples (replacing them with the previous effective sample).
If the ratios are low, tightening the priors may help.
Ratios all very high.
7 Model validation
Post predictive checks provide additional diagnostics about the fit of the model. Specifically, they provide a comparison between predictions drawn from the model and the observed data used to train the model.
bayesplot PPC module:
ppc_bars
ppc_bars_grouped
ppc_boxplot
ppc_dens
ppc_dens_overlay
ppc_dens_overlay_grouped
ppc_ecdf_overlay
ppc_ecdf_overlay_grouped
ppc_error_binned
ppc_error_hist
ppc_error_hist_grouped
ppc_error_scatter
ppc_error_scatter_avg
ppc_error_scatter_avg_grouped
ppc_error_scatter_avg_vs_x
ppc_freqpoly
ppc_freqpoly_grouped
ppc_hist
ppc_intervals
ppc_intervals_grouped
ppc_km_overlay
ppc_km_overlay_grouped
ppc_loo_intervals
ppc_loo_pit_ecdf
ppc_loo_pit_overlay
ppc_loo_pit_qq
ppc_loo_ribbon
ppc_pit_ecdf
ppc_pit_ecdf_grouped
ppc_ribbon
ppc_ribbon_grouped
ppc_rootogram
ppc_scatter
ppc_scatter_avg
ppc_scatter_avg_grouped
ppc_stat
ppc_stat_2d
ppc_stat_freqpoly
ppc_stat_freqpoly_grouped
ppc_stat_grouped
ppc_violin_grouped
- dens_overlay: plots the density distribution of the observed data (black line) overlayed on top of 50 density distributions generated from draws from the model (light blue). Ideally, the 50 realisations should be roughly consistent with the observed data.
Warning: Argument 'nsamples' is deprecated. Please use argument 'ndraws'
instead.
The model draws appear to be consistent with the observed data.
- error_scatter_avg: this plots the observed values against the average residuals. Similar to a residual plot, we do not want to see any patterns in this plot. Note, this is not really that useful for models that involve a binomial response
Using all posterior draws for ppc type 'error_scatter_avg' by default.
This is not really interpretable
- intervals: plots the observed data overlayed on top of posterior predictions associated with each level of the predictor. Ideally, the observed data should all fall within the predictive intervals.
Using all posterior draws for ppc type 'intervals' by default.
Warning: The following arguments were unrecognized and ignored: group
The shinystan package allows the full suite of MCMC diagnostics and posterior predictive checks to be accessed via a web interface.
DHARMa residuals provide very useful diagnostics. Unfortunately, we cannot directly use the simulateResiduals() function to generate the simulated residuals. However, if we are willing to calculate some of the components yourself, we can still obtain the simulated residuals from the fitted stan model.
We need to supply:
- simulated (predicted) responses associated with each observation.
- observed values
- fitted (predicted) responses (averaged) associated with each observation
Warning: Argument 'nsamples' is deprecated. Please use argument 'ndraws'
instead.
mckeon.resids <- createDHARMa(
simulatedResponse = t(preds),
observedResponse = mckeon$PREDATION,
fittedPredictedResponse = apply(preds, 2, median),
integerResponse = TRUE
)
plot(mckeon.resids, quantreg = TRUE)mckeon.resids <- make_brms_dharma_res(mckeon.brm4, integerResponse = FALSE)
wrap_elements(~ testUniformity(mckeon.resids)) +
wrap_elements(~ plotResiduals(mckeon.resids, form = factor(rep(1, nrow(mckeon))))) +
wrap_elements(~ plotResiduals(mckeon.resids, quantreg = FALSE)) +
wrap_elements(~ testDispersion(mckeon.resids))Conclusions:
- the simulated residuals do not suggest any issues with the residuals
- there is no evidence of a lack of fit.
8 Partial effects plots
Note: uncertainty of error terms are not taken into account. Consider
setting `interval` to "prediction". This will call `posterior_predict()`
instead of `posterior_epred()`.
Warning in glmmTMB::glmmTMB(formula = cond_formula, family = family, data =
data): some components missing from 'family': downstream methods may fail
Warning in glmmTMB::glmmTMB(formula = cond_formula, family = family, data =
data): some components missing from 'family': downstream methods may fail
Warning in glmmTMB::glmmTMB(formula = cond_formula, family = family, data =
data): some components missing from 'family': downstream methods may fail
Warning in glmmTMB::glmmTMB(formula = cond_formula, family = family, data =
data): some components missing from 'family': downstream methods may fail
Warning in glmmTMB::glmmTMB(formula = cond_formula, family = family, data =
data): some components missing from 'family': downstream methods may fail
Warning in glmmTMB::glmmTMB(formula = cond_formula, family = family, data =
data): some components missing from 'family': downstream methods may fail
## Partial residuals in binomial models are too confusing for the average viewer
## as they will yeild values that are not exactly 0 or 1 and this seems wrong.
## Partial.obs <- mckeon.brm3$data %>%
## mutate(Pred = predict(mckeon.brm3, re.form=NA)[,'Estimate'],
## Resid = resid(mckeon.brm3)[,'Estimate'],
## Obs = Pred + Resid)
mckeon.brm4 |>
epred_draws(newdata = mckeon, re_formula = NA) |>
median_hdci() |>
ggplot(aes(x = SYMBIONT, y = .epred)) +
geom_pointrange(aes(ymin = .lower, ymax = .upper)) +
geom_line() +
geom_point(
data = mckeon, aes(y = PREDATION, x = SYMBIONT),
alpha = 0.2,
position = position_jitter(width = 0.2, height = 0)
)9 Model investigation
The summary() method generates simple summaries (mean, standard deviation as well as 10, 50 and 90 percentiles).
Family: bernoulli
Links: mu = logit
Formula: PREDATION ~ SYMBIONT + (SYMBIONT | BLOCK)
Data: mckeon (Number of observations: 80)
Draws: 3 chains, each with iter = 5000; warmup = 1000; thin = 5;
total post-warmup draws = 2400
Multilevel Hyperparameters:
~BLOCK (Number of levels: 10)
Estimate Est.Error l-95% CI u-95% CI Rhat
sd(Intercept) 2.17 1.23 0.22 5.00 1.00
sd(SYMBIONTcrabs) 3.26 3.22 0.13 11.05 1.00
sd(SYMBIONTshrimp) 2.36 2.11 0.10 7.67 1.00
sd(SYMBIONTboth) 2.32 2.28 0.08 8.10 1.00
cor(Intercept,SYMBIONTcrabs) 0.30 0.39 -0.55 0.89 1.00
cor(Intercept,SYMBIONTshrimp) 0.26 0.41 -0.65 0.90 1.00
cor(SYMBIONTcrabs,SYMBIONTshrimp) 0.37 0.42 -0.62 0.92 1.01
cor(Intercept,SYMBIONTboth) 0.20 0.42 -0.68 0.88 1.00
cor(SYMBIONTcrabs,SYMBIONTboth) 0.28 0.42 -0.64 0.90 1.00
cor(SYMBIONTshrimp,SYMBIONTboth) 0.32 0.43 -0.68 0.92 1.00
Bulk_ESS Tail_ESS
sd(Intercept) 1694 1594
sd(SYMBIONTcrabs) 1494 2196
sd(SYMBIONTshrimp) 1988 2271
sd(SYMBIONTboth) 2014 2200
cor(Intercept,SYMBIONTcrabs) 2103 2266
cor(Intercept,SYMBIONTshrimp) 2298 2181
cor(SYMBIONTcrabs,SYMBIONTshrimp) 1944 2144
cor(Intercept,SYMBIONTboth) 2345 2364
cor(SYMBIONTcrabs,SYMBIONTboth) 2091 2204
cor(SYMBIONTshrimp,SYMBIONTboth) 2152 2370
Regression Coefficients:
Estimate Est.Error l-95% CI u-95% CI Rhat Bulk_ESS Tail_ESS
Intercept 3.07 1.16 1.15 5.65 1.00 2055 2410
SYMBIONTcrabs -1.43 1.68 -4.36 2.26 1.00 2110 2145
SYMBIONTshrimp -2.19 1.46 -4.89 0.92 1.00 1775 1925
SYMBIONTboth -3.32 1.45 -6.31 -0.38 1.00 2093 2178
Draws were sampled using sampling(NUTS). For each parameter, Bulk_ESS
and Tail_ESS are effective sample size measures, and Rhat is the potential
scale reduction factor on split chains (at convergence, Rhat = 1).
Conclusions:
- the coefficients are presented on a logit scale. Whilst this is not relevant for the purpose of inference testing, it does make it difficult to interpret the coefficients.
- if we exponentiate the coefficients (\(log(\frac{\rho}{1-\rho})\) -> \(\frac{\rho}{1-\rho}\)), they will be presented on a odds ratio scale, and thus:
- the intercept (none symbionts) will be 21.46. That is, corals without a symbiont are 21.46 times more likely to be predated on than not predated on. The odds of predation in this the absence of symbionts is 21.46:1.
- in the presence of a crab symbiont, the odds of being predated on are only 0.24 times that of the none symbiont group. That is, in the presence of a crab symbiont, the odds of predation decline by 76%.
- in the presence of a shrimp symbiont, the odds of being predated on are only 0.11 times that of the none symbiont group. That is, in the presence of a shrimp symbiont, the odds of predation decline by 89%.
- in the presence of both crab and shrimp symbionts, the odds of being predated on are only 0.04 times that of the none symbiont group. That is, in the presence of both crab and shrimp symbiont, the odds of predation decline by 96%.
- if we back-transform the intercept full to the response scale (probability scale), (\(log(\frac{\rho}{1-\rho})\) -> \(\rho\)), the intercept is interpreted as the probability that corals will be predated in the absence of of symbionts is 1
# A draws_df: 800 iterations, 3 chains, and 61 variables
b_Intercept b_SYMBIONTcrabs b_SYMBIONTshrimp b_SYMBIONTboth
1 3.5 2.91 -2.13 -2.9
2 2.3 0.50 -1.80 -2.6
3 1.7 1.82 -1.84 -2.1
4 5.0 -1.92 -1.41 -4.6
5 3.7 -4.39 -4.48 -3.1
6 1.7 1.71 -0.48 -3.7
7 2.6 -1.63 -4.25 -6.4
8 3.5 -2.78 -2.82 -4.2
9 2.5 -0.33 -1.52 -5.2
10 3.1 -3.13 -3.22 -3.9
sd_BLOCK__Intercept sd_BLOCK__SYMBIONTcrabs sd_BLOCK__SYMBIONTshrimp
1 1.57 5.32 0.81
2 0.88 8.85 1.09
3 1.96 16.11 1.91
4 4.47 0.52 2.11
5 7.95 0.51 2.76
6 1.07 9.47 1.64
7 1.72 0.75 0.55
8 1.40 2.86 1.00
9 0.76 2.92 2.09
10 2.18 3.96 0.30
sd_BLOCK__SYMBIONTboth
1 2.66
2 0.35
3 1.07
4 0.53
5 6.35
6 1.48
7 2.72
8 1.46
9 3.46
10 1.23
# ... with 2390 more draws, and 53 more variables
# ... hidden reserved variables {'.chain', '.iteration', '.draw'}
| variable | median | lower | upper | Pl | Pg | rhat | ess_bulk | ess_tail |
|---|---|---|---|---|---|---|---|---|
| b_Intercept | 19.4898474 | 0.6045049 | 1.632308e+02 | 0.0004167 | 0.9995833 | 0.9997882 | 2055.092 | 2409.901 |
| b_SYMBIONTcrabs | 0.2070936 | 0.0004330 | 4.507043e+00 | 0.8150000 | 0.1850000 | 1.0001200 | 2110.146 | 2145.482 |
| b_SYMBIONTshrimp | 0.1051186 | 0.0007097 | 1.359671e+00 | 0.9295833 | 0.0704167 | 1.0013649 | 1775.340 | 1925.299 |
| b_SYMBIONTboth | 0.0353588 | 0.0001376 | 3.982936e-01 | 0.9841667 | 0.0158333 | 1.0013479 | 2092.725 | 2178.352 |
| sd_BLOCK__Intercept | 7.7858769 | 1.0017286 | 7.386592e+01 | 0.0000000 | 1.0000000 | 1.0016444 | 1694.269 | 1594.410 |
| sd_BLOCK__SYMBIONTcrabs | 11.6013806 | 1.0039661 | 5.348673e+03 | 0.0000000 | 1.0000000 | 1.0031484 | 1494.461 | 2196.065 |
| sd_BLOCK__SYMBIONTshrimp | 6.2717197 | 1.0000851 | 5.115885e+02 | 0.0000000 | 1.0000000 | 1.0018677 | 1987.527 | 2271.175 |
| sd_BLOCK__SYMBIONTboth | 5.5787951 | 1.0000267 | 6.373033e+02 | 0.0000000 | 1.0000000 | 1.0002518 | 2013.781 | 2200.406 |
| cor_BLOCK__Intercept__SYMBIONTcrabs | 1.4361448 | 0.5467804 | 2.387722e+00 | 0.2245833 | 0.7754167 | 1.0024862 | 2102.692 | 2265.638 |
| cor_BLOCK__Intercept__SYMBIONTshrimp | 1.3695621 | 0.4908625 | 2.427816e+00 | 0.2575000 | 0.7425000 | 1.0008267 | 2297.981 | 2180.753 |
| cor_BLOCK__SYMBIONTcrabs__SYMBIONTshrimp | 1.5872139 | 0.5341720 | 2.511116e+00 | 0.1887500 | 0.8112500 | 1.0052500 | 1944.669 | 2143.888 |
| cor_BLOCK__Intercept__SYMBIONTboth | 1.2642661 | 0.4438061 | 2.282491e+00 | 0.3154167 | 0.6845833 | 1.0014627 | 2345.564 | 2363.818 |
| cor_BLOCK__SYMBIONTcrabs__SYMBIONTboth | 1.4063701 | 0.4789396 | 2.371325e+00 | 0.2433333 | 0.7566667 | 1.0002358 | 2091.271 | 2203.523 |
| cor_BLOCK__SYMBIONTshrimp__SYMBIONTboth | 1.5022249 | 0.4712434 | 2.470391e+00 | 0.2329167 | 0.7670833 | 0.9999984 | 2152.382 | 2369.526 |
| Intercept | 3.6362598 | 0.1230073 | 2.050830e+01 | 0.0791667 | 0.9208333 | 1.0015257 | 2286.548 | 2326.427 |
| r_BLOCK[1,Intercept] | 0.0815708 | 0.0003289 | 9.481156e-01 | 0.9620833 | 0.0379167 | 1.0005518 | 1715.449 | 1854.252 |
| r_BLOCK[2,Intercept] | 0.2062181 | 0.0003063 | 1.482728e+00 | 0.8883333 | 0.1116667 | 1.0007042 | 1853.909 | 2368.630 |
| r_BLOCK[3,Intercept] | 0.2137460 | 0.0001339 | 1.410428e+00 | 0.8950000 | 0.1050000 | 1.0018816 | 1885.827 | 2174.560 |
| r_BLOCK[4,Intercept] | 0.2243993 | 0.0002903 | 1.373790e+00 | 0.8970833 | 0.1029167 | 0.9992068 | 1839.651 | 2167.104 |
| r_BLOCK[5,Intercept] | 0.8984007 | 0.0034043 | 7.156857e+00 | 0.5462500 | 0.4537500 | 1.0009166 | 2120.588 | 2164.507 |
| r_BLOCK[6,Intercept] | 2.5170598 | 0.0326588 | 4.035546e+01 | 0.1937500 | 0.8062500 | 0.9996354 | 1866.804 | 2225.130 |
| r_BLOCK[7,Intercept] | 7.0092390 | 0.0435089 | 5.542687e+02 | 0.0920833 | 0.9079167 | 1.0003418 | 2000.375 | 2421.948 |
| r_BLOCK[8,Intercept] | 6.7055315 | 0.0978663 | 6.794157e+02 | 0.0875000 | 0.9125000 | 1.0004743 | 2004.067 | 2288.873 |
| r_BLOCK[9,Intercept] | 6.4276932 | 0.0443778 | 5.131682e+02 | 0.0841667 | 0.9158333 | 1.0033917 | 1865.168 | 1893.327 |
| r_BLOCK[10,Intercept] | 1.7970720 | 0.0065568 | 2.163980e+01 | 0.3000000 | 0.7000000 | 1.0003953 | 1944.647 | 2123.695 |
| r_BLOCK[1,SYMBIONTcrabs] | 0.1091017 | 0.0000000 | 1.804867e+00 | 0.8600000 | 0.1400000 | 1.0014140 | 1731.908 | 2368.207 |
| r_BLOCK[2,SYMBIONTcrabs] | 0.1035740 | 0.0000000 | 1.463403e+00 | 0.8787500 | 0.1212500 | 0.9997432 | 1917.953 | 2454.353 |
| r_BLOCK[3,SYMBIONTcrabs] | 0.1107821 | 0.0000000 | 1.458813e+00 | 0.8754167 | 0.1245833 | 1.0022731 | 1662.701 | 2250.793 |
| r_BLOCK[4,SYMBIONTcrabs] | 0.1003052 | 0.0000000 | 1.573084e+00 | 0.8675000 | 0.1325000 | 1.0014566 | 1867.693 | 2093.095 |
| r_BLOCK[5,SYMBIONTcrabs] | 1.6696494 | 0.0028625 | 3.883661e+02 | 0.3270833 | 0.6729167 | 1.0028783 | 2237.067 | 2289.464 |
| r_BLOCK[6,SYMBIONTcrabs] | 2.2984975 | 0.0050040 | 3.589090e+03 | 0.2729167 | 0.7270833 | 1.0005871 | 2087.195 | 2492.996 |
| r_BLOCK[7,SYMBIONTcrabs] | 3.7527496 | 0.0001332 | 4.147813e+04 | 0.2262500 | 0.7737500 | 0.9996911 | 2150.970 | 2315.729 |
| r_BLOCK[8,SYMBIONTcrabs] | 3.9132804 | 0.0000887 | 5.426021e+04 | 0.2200000 | 0.7800000 | 1.0014707 | 2161.248 | 2450.557 |
| r_BLOCK[9,SYMBIONTcrabs] | 4.1275947 | 0.0002635 | 4.291756e+04 | 0.2145833 | 0.7854167 | 1.0004711 | 1997.156 | 2305.570 |
| r_BLOCK[10,SYMBIONTcrabs] | 3.8991113 | 0.0048181 | 3.505546e+04 | 0.1966667 | 0.8033333 | 0.9998870 | 2203.243 | 2234.398 |
| r_BLOCK[1,SYMBIONTshrimp] | 0.2292724 | 0.0000000 | 2.207386e+00 | 0.8116667 | 0.1883333 | 1.0009479 | 1902.731 | 2158.368 |
| r_BLOCK[2,SYMBIONTshrimp] | 0.2351541 | 0.0000000 | 1.810802e+00 | 0.8266667 | 0.1733333 | 1.0003215 | 2033.008 | 2179.685 |
| r_BLOCK[3,SYMBIONTshrimp] | 0.2302034 | 0.0000000 | 1.643125e+00 | 0.8354167 | 0.1645833 | 1.0009086 | 1694.365 | 1979.737 |
| r_BLOCK[4,SYMBIONTshrimp] | 0.2211076 | 0.0000000 | 1.648815e+00 | 0.8391667 | 0.1608333 | 1.0016496 | 1941.789 | 2124.145 |
| r_BLOCK[5,SYMBIONTshrimp] | 0.8118627 | 0.0003214 | 6.443777e+00 | 0.6016667 | 0.3983333 | 0.9996430 | 2219.672 | 2215.179 |
| r_BLOCK[6,SYMBIONTshrimp] | 1.9783094 | 0.0009300 | 3.803152e+02 | 0.2758333 | 0.7241667 | 1.0003573 | 2084.789 | 2287.016 |
| r_BLOCK[7,SYMBIONTshrimp] | 2.6036762 | 0.0003320 | 3.030357e+03 | 0.2325000 | 0.7675000 | 1.0002810 | 2073.144 | 2329.077 |
| r_BLOCK[8,SYMBIONTshrimp] | 2.4391834 | 0.0036593 | 2.437851e+03 | 0.2425000 | 0.7575000 | 1.0013801 | 1956.243 | 2187.507 |
| r_BLOCK[9,SYMBIONTshrimp] | 2.6367647 | 0.0092494 | 3.348415e+03 | 0.2333333 | 0.7666667 | 1.0009221 | 1907.435 | 2084.156 |
| r_BLOCK[10,SYMBIONTshrimp] | 2.8906273 | 0.0217982 | 2.925741e+03 | 0.1937500 | 0.8062500 | 1.0009027 | 2041.581 | 2222.254 |
| r_BLOCK[1,SYMBIONTboth] | 0.3580806 | 0.0000000 | 2.985766e+00 | 0.7495833 | 0.2504167 | 1.0013357 | 1990.443 | 2066.486 |
| r_BLOCK[2,SYMBIONTboth] | 0.3497297 | 0.0000000 | 2.339799e+00 | 0.7779167 | 0.2220833 | 1.0005476 | 2139.529 | 2230.296 |
| r_BLOCK[3,SYMBIONTboth] | 0.3589003 | 0.0000000 | 2.440805e+00 | 0.7741667 | 0.2258333 | 0.9995410 | 2051.749 | 2160.395 |
| r_BLOCK[4,SYMBIONTboth] | 0.3675690 | 0.0000000 | 2.360844e+00 | 0.7779167 | 0.2220833 | 1.0004158 | 2072.553 | 2147.404 |
| r_BLOCK[5,SYMBIONTboth] | 0.5125846 | 0.0000000 | 2.439201e+00 | 0.7545833 | 0.2454167 | 0.9996884 | 2285.842 | 2370.382 |
| r_BLOCK[6,SYMBIONTboth] | 0.9835666 | 0.0000040 | 1.083872e+01 | 0.5166667 | 0.4833333 | 1.0010776 | 2411.078 | 2369.730 |
| r_BLOCK[7,SYMBIONTboth] | 2.5339575 | 0.0057559 | 2.622432e+03 | 0.2366667 | 0.7633333 | 0.9995157 | 2189.615 | 2260.810 |
| r_BLOCK[8,SYMBIONTboth] | 2.6504183 | 0.0018615 | 1.946967e+03 | 0.2370833 | 0.7629167 | 1.0017945 | 2059.261 | 2090.619 |
| r_BLOCK[9,SYMBIONTboth] | 2.5818989 | 0.0020219 | 2.103280e+03 | 0.2391667 | 0.7608333 | 1.0002435 | 2032.213 | 2366.702 |
| r_BLOCK[10,SYMBIONTboth] | 3.2837192 | 0.0307971 | 1.626532e+03 | 0.1733333 | 0.8266667 | 1.0003176 | 2132.097 | 2160.038 |
| prior_Intercept | 1.0544982 | 0.0000472 | 7.027993e+01 | 0.4908333 | 0.5091667 | 0.9999369 | 2537.456 | 2433.688 |
| prior_b | 0.7794361 | 0.0000589 | 1.381056e+02 | 0.5279167 | 0.4720833 | 0.9999476 | 1827.060 | 2164.417 |
| prior_sd_BLOCK | 3.1241502 | 1.0018341 | 1.487606e+02 | 0.0000000 | 1.0000000 | 1.0002197 | 2556.861 | 2394.074 |
| prior_cor_BLOCK | 1.0190180 | 0.3863294 | 2.062882e+00 | 0.4845833 | 0.5154167 | 0.9995468 | 2368.598 | 2328.842 |
| lprior | 0.0000000 | 0.0000000 | 0.000000e+00 | 1.0000000 | 0.0000000 | 1.0016575 | 1935.319 | 2222.751 |
| lp__ | 0.0000000 | 0.0000000 | 0.000000e+00 | 1.0000000 | 0.0000000 | 1.0004049 | 2272.767 | 2270.003 |
Warning: Dropping 'draws_df' class as required metadata was removed.
| variable | median | lower | upper | Pl | Pg | rhat | ess_bulk | ess_tail |
|---|---|---|---|---|---|---|---|---|
| b_Intercept | 19.4898474 | 0.6045049 | 163.2307858 | 0.0004167 | 0.9995833 | 1.0007028 | 2036.007 | 2402.110 |
| b_SYMBIONTcrabs | 0.2070936 | 0.0004330 | 4.5070426 | 0.8150000 | 0.1850000 | 1.0006845 | 2097.514 | 2156.583 |
| b_SYMBIONTshrimp | 0.1051186 | 0.0007097 | 1.3596712 | 0.9295833 | 0.0704167 | 1.0012607 | 1750.729 | 1855.157 |
| b_SYMBIONTboth | 0.0353588 | 0.0001376 | 0.3982936 | 0.9841667 | 0.0158333 | 1.0009438 | 2073.024 | 2109.557 |
| sd_BLOCK__Intercept | 7.7858769 | 1.0017286 | 73.8659205 | 0.0000000 | 1.0000000 | 1.0009991 | 1679.408 | 1558.746 |
| sd_BLOCK__SYMBIONTcrabs | 11.6013806 | 1.0039661 | 5348.6733889 | 0.0000000 | 1.0000000 | 0.9999464 | 1476.002 | 2167.789 |
| sd_BLOCK__SYMBIONTshrimp | 6.2717197 | 1.0000851 | 511.5884505 | 0.0000000 | 1.0000000 | 0.9997040 | 1965.188 | 2181.840 |
| sd_BLOCK__SYMBIONTboth | 5.5787951 | 1.0000267 | 637.3032532 | 0.0000000 | 1.0000000 | 1.0001018 | 2004.852 | 2193.817 |
mckeon.brm4$fit |>
tidyMCMC(
estimate.method = "median",
conf.int = TRUE, conf.method = "HPDinterval",
rhat = TRUE, ess = TRUE
)# A tibble: 60 × 7
term estimate std.error conf.low conf.high rhat ess
<chr> <dbl> <dbl> <dbl> <dbl> <dbl> <int>
1 b_Intercept 3.07 1.16 9.42e-1 5.34 1.000 2000
2 b_SYMBIONTcrabs -1.43 1.68 -4.57e+0 1.93 1.00 2133
3 b_SYMBIONTshrimp -2.19 1.46 -5.13e+0 0.665 1.00 1763
4 b_SYMBIONTboth -3.32 1.45 -6.44e+0 -0.553 1.00 2105
5 sd_BLOCK__Intercept 2.17 1.23 3.56e-2 4.32 1.00 1772
6 sd_BLOCK__SYMBIONTcrabs 3.26 3.22 3.96e-3 8.58 1.00 2050
7 sd_BLOCK__SYMBIONTshrimp 2.36 2.11 8.51e-5 6.24 1.000 1974
8 sd_BLOCK__SYMBIONTboth 2.32 2.28 2.67e-5 6.46 1.00 2007
9 cor_BLOCK__Intercept__SYMB… 0.304 0.385 -4.23e-1 0.950 1.00 2027
10 cor_BLOCK__Intercept__SYMB… 0.263 0.412 -5.12e-1 0.969 1.00 2277
# ℹ 50 more rows
Conclusions:
see above
Due to the presence of a log transform in the predictor, it is better to use the regex version.
[1] "b_Intercept"
[2] "b_SYMBIONTcrabs"
[3] "b_SYMBIONTshrimp"
[4] "b_SYMBIONTboth"
[5] "sd_BLOCK__Intercept"
[6] "sd_BLOCK__SYMBIONTcrabs"
[7] "sd_BLOCK__SYMBIONTshrimp"
[8] "sd_BLOCK__SYMBIONTboth"
[9] "cor_BLOCK__Intercept__SYMBIONTcrabs"
[10] "cor_BLOCK__Intercept__SYMBIONTshrimp"
[11] "cor_BLOCK__SYMBIONTcrabs__SYMBIONTshrimp"
[12] "cor_BLOCK__Intercept__SYMBIONTboth"
[13] "cor_BLOCK__SYMBIONTcrabs__SYMBIONTboth"
[14] "cor_BLOCK__SYMBIONTshrimp__SYMBIONTboth"
[15] "Intercept"
[16] "r_BLOCK[1,Intercept]"
[17] "r_BLOCK[2,Intercept]"
[18] "r_BLOCK[3,Intercept]"
[19] "r_BLOCK[4,Intercept]"
[20] "r_BLOCK[5,Intercept]"
[21] "r_BLOCK[6,Intercept]"
[22] "r_BLOCK[7,Intercept]"
[23] "r_BLOCK[8,Intercept]"
[24] "r_BLOCK[9,Intercept]"
[25] "r_BLOCK[10,Intercept]"
[26] "r_BLOCK[1,SYMBIONTcrabs]"
[27] "r_BLOCK[2,SYMBIONTcrabs]"
[28] "r_BLOCK[3,SYMBIONTcrabs]"
[29] "r_BLOCK[4,SYMBIONTcrabs]"
[30] "r_BLOCK[5,SYMBIONTcrabs]"
[31] "r_BLOCK[6,SYMBIONTcrabs]"
[32] "r_BLOCK[7,SYMBIONTcrabs]"
[33] "r_BLOCK[8,SYMBIONTcrabs]"
[34] "r_BLOCK[9,SYMBIONTcrabs]"
[35] "r_BLOCK[10,SYMBIONTcrabs]"
[36] "r_BLOCK[1,SYMBIONTshrimp]"
[37] "r_BLOCK[2,SYMBIONTshrimp]"
[38] "r_BLOCK[3,SYMBIONTshrimp]"
[39] "r_BLOCK[4,SYMBIONTshrimp]"
[40] "r_BLOCK[5,SYMBIONTshrimp]"
[41] "r_BLOCK[6,SYMBIONTshrimp]"
[42] "r_BLOCK[7,SYMBIONTshrimp]"
[43] "r_BLOCK[8,SYMBIONTshrimp]"
[44] "r_BLOCK[9,SYMBIONTshrimp]"
[45] "r_BLOCK[10,SYMBIONTshrimp]"
[46] "r_BLOCK[1,SYMBIONTboth]"
[47] "r_BLOCK[2,SYMBIONTboth]"
[48] "r_BLOCK[3,SYMBIONTboth]"
[49] "r_BLOCK[4,SYMBIONTboth]"
[50] "r_BLOCK[5,SYMBIONTboth]"
[51] "r_BLOCK[6,SYMBIONTboth]"
[52] "r_BLOCK[7,SYMBIONTboth]"
[53] "r_BLOCK[8,SYMBIONTboth]"
[54] "r_BLOCK[9,SYMBIONTboth]"
[55] "r_BLOCK[10,SYMBIONTboth]"
[56] "prior_Intercept"
[57] "prior_b"
[58] "prior_sd_BLOCK"
[59] "prior_cor_BLOCK"
[60] "lprior"
[61] "lp__"
[62] "accept_stat__"
[63] "stepsize__"
[64] "treedepth__"
[65] "n_leapfrog__"
[66] "divergent__"
[67] "energy__"
# A tibble: 9,600 × 5
# Groups: .variable [4]
.chain .iteration .draw .variable .value
<int> <int> <int> <chr> <dbl>
1 1 1 1 b_Intercept 3.54
2 1 2 2 b_Intercept 2.33
3 1 3 3 b_Intercept 1.69
4 1 4 4 b_Intercept 5.00
5 1 5 5 b_Intercept 3.70
6 1 6 6 b_Intercept 1.70
7 1 7 7 b_Intercept 2.61
8 1 8 8 b_Intercept 3.49
9 1 9 9 b_Intercept 2.52
10 1 10 10 b_Intercept 3.10
# ℹ 9,590 more rows
We can then summarise this
# A tibble: 4 × 7
.variable .value .lower .upper .width .point .interval
<chr> <dbl> <dbl> <dbl> <dbl> <chr> <chr>
1 b_Intercept 2.97 0.998 5.43 0.95 median hdci
2 b_SYMBIONTboth -3.34 -6.49 -0.601 0.95 median hdci
3 b_SYMBIONTcrabs -1.57 -4.42 2.10 0.95 median hdci
4 b_SYMBIONTshrimp -2.25 -4.97 0.831 0.95 median hdci
# A tibble: 4 × 7
.variable .value .lower .upper .width .point .interval
<chr> <dbl> <dbl> <dbl> <dbl> <chr> <chr>
1 b_Intercept 19.5 0.605 163. 0.95 median hdci
2 b_SYMBIONTboth 0.0354 0.000138 0.394 0.95 median hdci
3 b_SYMBIONTcrabs 0.207 0.000433 4.50 0.95 median hdci
4 b_SYMBIONTshrimp 0.105 0.000710 1.36 0.95 median hdci
mckeon.brm4 |>
gather_draws(`b_Intercept.*|b_SYMBIONT.*`, regex = TRUE) |>
ggplot() +
geom_vline(xintercept = 0, linetype = "dashed") +
stat_slab(aes(
x = .value, y = .variable,
fill = stat(ggdist::cut_cdf_qi(cdf,
.width = c(0.5, 0.8, 0.95),
labels = scales::percent_format()
))
), color = "black") +
scale_fill_brewer("Interval", direction = -1, na.translate = FALSE)Warning: `stat(ggdist::cut_cdf_qi(cdf, .width = c(0.5, 0.8, 0.95), labels =
scales::percent_format()))` was deprecated in ggplot2 3.4.0.
ℹ Please use `after_stat(ggdist::cut_cdf_qi(cdf, .width = c(0.5, 0.8, 0.95),
labels = scales::percent_format()))` instead.
mckeon.brm4 |>
gather_draws(`.Intercept.*|b_SYMBIONT.*`, regex = TRUE) |>
ggplot() +
geom_vline(xintercept = 0, linetype = "dashed") +
stat_halfeye(aes(x = .value, y = .variable)) +
theme_classic()mckeon.brm4 |>
gather_draws(`^b_.*`, regex = TRUE) |>
filter(.variable != "b_Intercept") |>
ggplot() +
stat_halfeye(aes(x = .value, y = .variable)) +
facet_wrap(~.variable, scales = "free")mckeon.brm4 |>
gather_draws(`^b_.*`, regex = TRUE) |>
filter(.variable != "b_Intercept") |>
ggplot() +
stat_halfeye(aes(x = .value, y = .variable)) +
geom_vline(xintercept = 0, linetype = "dashed")mckeon.brm4 |>
gather_draws(`^b_.*`, regex = TRUE) |>
filter(.variable != "b_Intercept") |>
ggplot() +
geom_density_ridges(aes(x = .value, y = .variable), alpha = 0.4) +
geom_vline(xintercept = 0, linetype = "dashed")Picking joint bandwidth of 0.275
## Or in colour
mckeon.brm4 |>
gather_draws(`^b_.*`, regex = TRUE) |>
filter(.variable != "b_Intercept") |>
ggplot() +
geom_density_ridges_gradient(
aes(
x = (.value),
y = .variable,
fill = stat(x)
),
alpha = 0.4, colour = "white",
quantile_lines = TRUE,
quantiles = c(0.025, 0.975)
) +
geom_vline(xintercept = 1, linetype = "dashed") +
scale_x_continuous() +
scale_fill_viridis_c(option = "C")Picking joint bandwidth of 0.275
## Fractional scale
mckeon.brm4 |>
gather_draws(`^b_.*`, regex = TRUE) |>
filter(.variable != "b_Intercept") |>
ggplot() +
geom_density_ridges_gradient(
aes(
x = exp(.value),
y = .variable,
fill = stat(x)
),
alpha = 0.4, colour = "white",
quantile_lines = TRUE,
quantiles = c(0.025, 0.975)
) +
geom_vline(xintercept = 1, linetype = "dashed") +
scale_x_continuous(trans = scales::log2_trans()) +
scale_fill_viridis_c(option = "C")Picking joint bandwidth of 0.397
# A tibble: 2,400 × 70
.chain .iteration .draw b_Intercept b_SYMBIONTcrabs b_SYMBIONTshrimp
<int> <int> <int> <dbl> <dbl> <dbl>
1 1 1 1 3.54 2.91 -2.13
2 1 2 2 2.33 0.500 -1.80
3 1 3 3 1.69 1.82 -1.84
4 1 4 4 5.00 -1.92 -1.41
5 1 5 5 3.70 -4.39 -4.48
6 1 6 6 1.70 1.71 -0.478
7 1 7 7 2.61 -1.63 -4.25
8 1 8 8 3.49 -2.78 -2.82
9 1 9 9 2.52 -0.332 -1.52
10 1 10 10 3.10 -3.13 -3.22
# ℹ 2,390 more rows
# ℹ 64 more variables: b_SYMBIONTboth <dbl>, sd_BLOCK__Intercept <dbl>,
# sd_BLOCK__SYMBIONTcrabs <dbl>, sd_BLOCK__SYMBIONTshrimp <dbl>,
# sd_BLOCK__SYMBIONTboth <dbl>, cor_BLOCK__Intercept__SYMBIONTcrabs <dbl>,
# cor_BLOCK__Intercept__SYMBIONTshrimp <dbl>,
# cor_BLOCK__SYMBIONTcrabs__SYMBIONTshrimp <dbl>,
# cor_BLOCK__Intercept__SYMBIONTboth <dbl>, …
# A tibble: 2,400 × 23
.chain .iteration .draw b_Intercept b_SYMBIONTcrabs b_SYMBIONTshrimp
<int> <int> <int> <dbl> <dbl> <dbl>
1 1 1 1 3.54 2.91 -2.13
2 1 2 2 2.33 0.500 -1.80
3 1 3 3 1.69 1.82 -1.84
4 1 4 4 5.00 -1.92 -1.41
5 1 5 5 3.70 -4.39 -4.48
6 1 6 6 1.70 1.71 -0.478
7 1 7 7 2.61 -1.63 -4.25
8 1 8 8 3.49 -2.78 -2.82
9 1 9 9 2.52 -0.332 -1.52
10 1 10 10 3.10 -3.13 -3.22
# ℹ 2,390 more rows
# ℹ 17 more variables: b_SYMBIONTboth <dbl>, sd_BLOCK__Intercept <dbl>,
# cor_BLOCK__Intercept__SYMBIONTcrabs <dbl>,
# cor_BLOCK__Intercept__SYMBIONTshrimp <dbl>,
# cor_BLOCK__Intercept__SYMBIONTboth <dbl>, Intercept <dbl>,
# `r_BLOCK[1,Intercept]` <dbl>, `r_BLOCK[2,Intercept]` <dbl>,
# `r_BLOCK[3,Intercept]` <dbl>, `r_BLOCK[4,Intercept]` <dbl>, …
Warning: Method 'posterior_samples' is deprecated. Please see ?as_draws for
recommended alternatives.
# A tibble: 2,400 × 61
b_Intercept b_SYMBIONTcrabs b_SYMBIONTshrimp b_SYMBIONTboth
<dbl> <dbl> <dbl> <dbl>
1 3.54 2.91 -2.13 -2.89
2 2.33 0.500 -1.80 -2.65
3 1.69 1.82 -1.84 -2.08
4 5.00 -1.92 -1.41 -4.58
5 3.70 -4.39 -4.48 -3.07
6 1.70 1.71 -0.478 -3.69
7 2.61 -1.63 -4.25 -6.37
8 3.49 -2.78 -2.82 -4.16
9 2.52 -0.332 -1.52 -5.20
10 3.10 -3.13 -3.22 -3.87
# ℹ 2,390 more rows
# ℹ 57 more variables: sd_BLOCK__Intercept <dbl>,
# sd_BLOCK__SYMBIONTcrabs <dbl>, sd_BLOCK__SYMBIONTshrimp <dbl>,
# sd_BLOCK__SYMBIONTboth <dbl>, cor_BLOCK__Intercept__SYMBIONTcrabs <dbl>,
# cor_BLOCK__Intercept__SYMBIONTshrimp <dbl>,
# cor_BLOCK__SYMBIONTcrabs__SYMBIONTshrimp <dbl>,
# cor_BLOCK__Intercept__SYMBIONTboth <dbl>, …
y ymin ymax .width .point .interval
1 0.174463 0.004768317 0.3150586 0.95 median hdci
y ymin ymax .width .point .interval
1 0.4774058 0.1171811 0.730853 0.95 median hdci
## if we had random intercept/slope
mckeon.brm4 |>
bayes_R2(re.form = ~ (SYMBIONT | BLOCK), summary = FALSE) |>
median_hdci() y ymin ymax .width .point .interval
1 0.7037594 0.5693019 0.8187142 0.95 median hdci
mckeon.brm4 |> modelsummary(
statistic = c("conf.low", "conf.high"),
shape = term ~ statistic,
exponentiate = FALSE
)Warning:
`modelsummary` uses the `performance` package to extract goodness-of-fit
statistics from models of this class. You can specify the statistics you wish
to compute by supplying a `metrics` argument to `modelsummary`, which will then
push it forward to `performance`. Acceptable values are: "all", "common",
"none", or a character vector of metrics names. For example: `modelsummary(mod,
metrics = c("RMSE", "R2")` Note that some metrics are computationally
expensive. See `?performance::performance` for details.
This warning appears once per session.
| (1) | |||
|---|---|---|---|
| Est. | 2.5 % | 97.5 % | |
| b_Intercept | 2.970 | 1.149 | 5.646 |
| b_SYMBIONTcrabs | -1.575 | -4.358 | 2.264 |
| b_SYMBIONTshrimp | -2.253 | -4.891 | 0.924 |
| b_SYMBIONTboth | -3.342 | -6.312 | -0.384 |
| sd_BLOCK__Intercept | 2.052 | 0.223 | 4.996 |
| sd_BLOCK__SYMBIONTcrabs | 2.451 | 0.132 | 11.049 |
| sd_BLOCK__SYMBIONTshrimp | 1.836 | 0.102 | 7.668 |
| sd_BLOCK__SYMBIONTboth | 1.719 | 0.078 | 8.101 |
| cor_BLOCK__Intercept__SYMBIONTcrabs | 0.362 | -0.551 | 0.891 |
| cor_BLOCK__Intercept__SYMBIONTshrimp | 0.314 | -0.652 | 0.903 |
| cor_BLOCK__SYMBIONTcrabs__SYMBIONTshrimp | 0.462 | -0.624 | 0.923 |
| cor_BLOCK__Intercept__SYMBIONTboth | 0.234 | -0.682 | 0.875 |
| cor_BLOCK__SYMBIONTcrabs__SYMBIONTboth | 0.341 | -0.644 | 0.896 |
| cor_BLOCK__SYMBIONTshrimp__SYMBIONTboth | 0.407 | -0.676 | 0.924 |
| Num.Obs. | 80 | ||
| R2 | 0.704 | ||
| R2 Marg. | 0.174 | ||
| ICC | 0.9 | ||
| ELPD | -26.4 | ||
| ELPD s.e. | 6.5 | ||
| LOOIC | 52.7 | ||
| LOOIC s.e. | 13.0 | ||
| WAIC | 50.0 | ||
| RMSE | 0.21 | ||
10 Further analyses
In addition to comparing each of the symbiont types against the control group of no symbionts, it might be interesting to investigate whether there are any differences between the predation protection provided by crabs and shrimp, as well as whether having both crabs and shrimp symbionts is different to only a single symbiont type.
These contrasts can be explored via specific contrasts.
| SYMBIONT | Crab vs Shrimp | One vs Both | None vs Symbiont |
|---|---|---|---|
| none | 0 | 0 | 1 |
| crab | 1 | 1/2 | -1/3 |
| shrimp | -1 | 1/2 | -1/3 |
| both | 0 | -1 | -1/3 |
Warning: Method 'parse_bf' is deprecated. Please use 'brmsterms' instead.
contrast odds.ratio lower.HPD upper.HPD
none / crabs 4.83 0.00179 50.9
none / shrimp 9.51 0.01256 82.5
none / both 28.28 0.01766 306.4
crabs / shrimp 1.95 0.00104 45.7
crabs / both 6.12 0.00825 138.2
shrimp / both 2.99 0.00397 43.5
Point estimate displayed: median
Results are back-transformed from the log odds ratio scale
HPD interval probability: 0.95
# via tidy_draws
mckeon.em <-
mckeon.brm4 |>
emmeans(~SYMBIONT, type = "link") |>
pairs() |>
tidy_draws() |>
mutate(across(starts_with("contrast"), exp)) |>
summarise_draws(median,
HDInterval::hdi,
Pl = ~ mean(.x < 1),
Pg = ~ mean(.x > 1)
)Warning: Method 'parse_bf' is deprecated. Please use 'brmsterms' instead.
# A tibble: 6 × 6
variable median lower upper Pl Pg
<chr> <dbl> <dbl> <dbl> <dbl> <dbl>
1 contrast none - crabs 4.83 0.00179 50.9 0.185 0.815
2 contrast none - shrimp 9.51 0.0126 82.5 0.0704 0.930
3 contrast none - both 28.3 0.0177 306. 0.0158 0.984
4 contrast crabs - shrimp 1.95 0.00104 45.7 0.334 0.666
5 contrast crabs - both 6.12 0.00825 138. 0.117 0.883
6 contrast shrimp - both 2.99 0.00397 43.5 0.218 0.782
# or via gather_emmeans_draws()
mckeon.em <-
mckeon.brm4 |>
emmeans(~SYMBIONT, type = "link") |>
pairs() |>
gather_emmeans_draws() |>
mutate(.value = exp(.value)) |>
summarise(median_hdci(.value),
Pl = mean(.value < 1),
Pg = mean(.value > 1)
)Warning: Method 'parse_bf' is deprecated. Please use 'brmsterms' instead.
# A tibble: 6 × 9
contrast y ymin ymax .width .point .interval Pl Pg
<chr> <dbl> <dbl> <dbl> <dbl> <chr> <chr> <dbl> <dbl>
1 crabs - both 6.12 0.00825 138. 0.95 median hdci 0.117 0.883
2 crabs - shrimp 1.95 0.00104 45.5 0.95 median hdci 0.334 0.666
3 none - both 28.3 0.0177 306. 0.95 median hdci 0.0158 0.984
4 none - crabs 4.83 0.00179 50.8 0.95 median hdci 0.185 0.815
5 none - shrimp 9.51 0.0126 82.0 0.95 median hdci 0.0704 0.930
6 shrimp - both 2.99 0.00397 43.4 0.95 median hdci 0.218 0.782
## On a probability scale
mckeon.em <-
mckeon.brm4 |>
emmeans(~SYMBIONT, type = "link") |>
regrid() |>
pairs() |>
tidy_draws() |>
summarise_draws(median,
HDInterval::hdi,
Pl = ~ mean(.x < 0),
Pg = ~ mean(.x > 0)
)Warning: Method 'parse_bf' is deprecated. Please use 'brmsterms' instead.
# A tibble: 6 × 6
variable median lower upper Pl Pg
<chr> <dbl> <dbl> <dbl> <dbl> <dbl>
1 contrast none - crabs 0.121 -0.114 0.643 0.185 0.815
2 contrast none - shrimp 0.245 -0.0717 0.740 0.0704 0.930
3 contrast none - both 0.494 0.0469 0.896 0.0158 0.984
4 contrast crabs - shrimp 0.0822 -0.434 0.619 0.334 0.666
5 contrast crabs - both 0.307 -0.216 0.835 0.117 0.883
6 contrast shrimp - both 0.206 -0.293 0.742 0.218 0.782
# or via gather_emmeans_draws()
## mckeon.em <- mckeon.brm4 |>
## emmeans(~SYMBIONT, type='link') |>
## pairs() |>
## gather_emmeans_draws() |>
## mutate(Eff=exp(.value),
## PEff=100*(Eff-1))#,
## # Prob = plogis(.value))
## mckeon.em |> head()
## mckeon.em |>
## group_by(contrast) |>
## dplyr::select(contrast, Eff) |>
## median_hdi()
## mckeon.em |>
## group_by(contrast) |>
## summarize(Prob=sum(Eff>1)/n())
## On a probability scale
mckeon.em <- mckeon.brm4 |>
emmeans(~SYMBIONT, type = "link") |>
regrid() |>
pairs() |>
gather_emmeans_draws() |>
mutate(Eff = .value) # ,Warning: Method 'parse_bf' is deprecated. Please use 'brmsterms' instead.
# A tibble: 6 × 6
# Groups: contrast [1]
contrast .chain .iteration .draw .value Eff
<chr> <int> <int> <int> <dbl> <dbl>
1 none - crabs NA NA 1 -0.0265 -0.0265
2 none - crabs NA NA 2 -0.0328 -0.0328
3 none - crabs NA NA 3 -0.127 -0.127
4 none - crabs NA NA 4 0.0374 0.0374
5 none - crabs NA NA 5 0.643 0.643
6 none - crabs NA NA 6 -0.122 -0.122
# A tibble: 6 × 7
contrast Eff .lower .upper .width .point .interval
<chr> <dbl> <dbl> <dbl> <dbl> <chr> <chr>
1 crabs - both 0.307 -0.209 0.845 0.95 median hdi
2 crabs - shrimp 0.0822 -0.471 0.586 0.95 median hdi
3 none - both 0.494 0.0344 0.885 0.95 median hdi
4 none - crabs 0.121 -0.114 0.643 0.95 median hdi
5 none - shrimp 0.245 -0.0656 0.746 0.95 median hdi
6 shrimp - both 0.206 -0.313 0.726 0.95 median hdi
Warning: Method 'parse_bf' is deprecated. Please use 'brmsterms' instead.
# A tibble: 4 × 7
SYMBIONT P .lower .upper .width .point .interval
<fct> <dbl> <dbl> <dbl> <dbl> <chr> <chr>
1 none 0.951 0.798 0.999 0.95 median hdci
2 crabs 0.818 0.320 1.000 0.95 median hdci
3 shrimp 0.693 0.204 0.998 0.95 median hdci
4 both 0.438 0.0184 0.887 0.95 median hdci
cmat <- cbind(
"Crab vs shrimp" = c(0, 1, -1, 0),
"Both vs One" = c(0, -1 / 2, -1 / 2, 1),
"Any vs None" = c(-1, 1 / 3, 1 / 3, 1 / 3)
)
mckeon.em <- mckeon.brm4 |>
emmeans(~SYMBIONT, type = "response") |>
contrast(method = list(cmat))Warning: Method 'parse_bf' is deprecated. Please use 'brmsterms' instead.
mckeon.em <-
mckeon.brm4 |>
emmeans(~SYMBIONT, type = "link") |>
contrast(method = list(cmat)) |>
tidy_draws() |>
mutate(across(starts_with("contrast"), exp)) |>
summarise_draws(median,
HDInterval::hdi,
Pl = ~ mean(.x < 1),
Pg = ~ mean(.x > 1)
)Warning: Method 'parse_bf' is deprecated. Please use 'brmsterms' instead.
# A tibble: 3 × 6
variable median lower upper Pl Pg
<chr> <dbl> <dbl> <dbl> <dbl> <dbl>
1 contrast Crab vs shrimp 1.95 0.00104 45.7 0.334 0.666
2 contrast Both vs One 0.236 0.00104 2.13 0.887 0.113
3 contrast Any vs None 0.0933 0.00125 0.685 0.971 0.0288
## or via gather_emmeans_draws
mckeon.em <-
mckeon.brm4 |>
emmeans(~SYMBIONT, type = "link") |>
contrast(method = list(cmat)) |>
gather_emmeans_draws() |>
mutate(Fit = exp(.value)) |>
summarise(median_hdci(Fit),
Pl = mean(Fit < 0),
Pg = mean(Fit > 0)
)Warning: Method 'parse_bf' is deprecated. Please use 'brmsterms' instead.
# A tibble: 3 × 9
contrast y ymin ymax .width .point .interval Pl Pg
<chr> <dbl> <dbl> <dbl> <dbl> <chr> <chr> <dbl> <dbl>
1 Any vs None 0.0933 0.00289 0.689 0.95 median hdci 0 1
2 Both vs One 0.236 0.00104 2.13 0.95 median hdci 0 1
3 Crab vs shrimp 1.95 0.00104 45.5 0.95 median hdci 0 1
Warning: Method 'parse_bf' is deprecated. Please use 'brmsterms' instead.
SYMBIONT response lower.HPD upper.HPD
none 0.9511953 0.8018662 0.9997149
crabs 0.8177095 0.3196022 0.9997197
shrimp 0.6926152 0.2037201 0.9975018
both 0.4384410 0.0183692 0.8872965
Point estimate displayed: median
Results are back-transformed from the logit scale
HPD interval probability: 0.95
ggplot(newdata, aes(y = response, x = SYMBIONT)) +
geom_pointrange(aes(ymin = lower.HPD, ymax = upper.HPD)) Estimate Est.Error Q2.5 Q97.5
R2 0.1698209 0.09045373 0.01086722 0.3231581
y ymin ymax .width .point .interval
1 0.174463 0.004768317 0.3150586 0.95 median hdci
y ymin ymax .width .point .interval
1 0.4774058 0.1171811 0.730853 0.95 median hdci
## for random intercept/slope model
mckeon.brm4 |>
bayes_R2(re.form = ~ (SYMBIONT | BLOCK), summary = FALSE) |>
median_hdci() y ymin ymax .width .point .interval
1 0.7037594 0.5693019 0.8187142 0.95 median hdci